/*
 * Sphere.java 2011. 5. 30.
 *
 * Copyright 2011 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.naver.geotools;

/**
 * @author kr12250
 */
public class Sphere {
	public static final Sphere WGS84 = new Sphere(6371.0 * 1000, 6378.1 * 1000, 6356.8 * 1000);
	public final double meanRadius;
	public final double equatorialRadius;
	public final double polarRadius;
	private final double metersPerLatitude;
	private final double metersPerLongitude;

	public Sphere(double meanRadius, double equatorialRadius, double polarRadius) {
		this.meanRadius = meanRadius;
		this.equatorialRadius = equatorialRadius;
		this.polarRadius = polarRadius;
		metersPerLatitude = (2 * Math.PI * polarRadius) / 360;
		metersPerLongitude = (2 * Math.PI * equatorialRadius) / 360;
	}

	public double latitudeToMeter(double degree) {
		return degree * metersPerLatitude;
	}

	public double meterToLatitude(double meter) {
		return meter / metersPerLatitude;
	}

	public double longitudeToMeter(double degree, double latitude) {
		return degree * (metersPerLongitude * Math.cos(Math.toRadians(latitude)));
	}

	public double meterToLongitude(double meter, double latitude) {
		return meter / (metersPerLongitude * Math.cos(Math.toRadians(latitude)));
	}

	/**
	 * Use the haversine formula.
	 * 
	 * http://www.movable-type.co.uk/scripts/gis-faq-5.1.html
	 * http://www.consultsarath.com/contents/articles/KB000012-distance-between-two-points-on-globe--calculation-using-cSharp.aspx
	 * http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
	 */
	public double distance(Coordinate from, Coordinate to) {
		return meanRadius * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(Math.toRadians(from.latitude - to.latitude) / 2), 2) + Math.cos(Math.toRadians(from.latitude)) * Math.cos(Math.toRadians(to.latitude)) * Math.pow(Math.sin(Math.toRadians(from.longitude - to.longitude) / 2), 2)));
	}

	public double pan(Coordinate f, Coordinate other) {
		double dx = longitudeToMeter(other.longitude - f.longitude, (f.latitude + other.latitude / 2));
		double dy = latitudeToMeter(other.latitude - f.latitude);
		double distance = Math.sqrt(dx * dx + dy * dy);
		if (distance == 0.0) {
			return 0.0F;
		}
		double degree = Math.toDegrees(Math.acos(dy / distance));
		degree = (other.longitude > f.longitude) ? degree : -degree;
		if (degree < -180.00) {
			degree = -180.00;
		} else if (degree > 180.00) {
			degree = 180.00;
		}
		return degree;
	}
}
